/*
 * Copyright 2010 - 2011, The PLDesktop Development Team
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <tinfo.h>

namespace __cxxabiv1 {

	/**
	 * this is the external interface to the dynamic cast machinery
	 * sub: source address to be adjusted; nonnull, and since the
	 *      source object is polymorphic, *(void**)sub is a virtual pointer.
	 * src: static type of the source object.
	 * dst: destination type (the "T" in "dynamic_cast<T>(v)").
	 * src2dst_offset: a static hint about the location of the
	 *    source subobject with respect to the complete object;
	 *    special negative values are:
	 *       -1: no hint
	 *       -2: src is not a public base of dst
	 *       -3: src is a multiple public base type but never a
	 *           virtual base type
	 *    otherwise, the src type is a unique public nonvirtual
	 *    base type of dst at offset src2dst_offset from the
	 *    origin of dst.
	 *
	 * @param src_ptr object started from.
	 * @param src_type type of the starting object.
	 * @param dst_type desired target type.
	 * @param src2dst how src and dst are related.
	 */
	extern "C" void *__dynamic_cast (const void *src_ptr, const __class_type_info *src_type, const __class_type_info *dst_type, ptrdiff_t src2dst) {
		const void *vtable = *static_cast <const void *const *> (src_ptr);
		const vtable_prefix *prefix = adjust_pointer <vtable_prefix> (vtable, -offsetof (vtable_prefix, origin));
		const void *whole_ptr = adjust_pointer <void> (src_ptr, prefix->whole_object);
		const __class_type_info *whole_type = prefix->whole_type;
		__class_type_info::__dyncast_result result;
		whole_type->__do_dyncast (src2dst, __class_type_info::__contained_public, dst_type, whole_ptr, src_type, src_ptr, result);
		if(!result.dst_ptr) {
			return NULL;
		}
		if(contained_public_p (result.dst2src)) {
			return const_cast <void *> (result.dst_ptr);
		}
		if(contained_public_p (__class_type_info::__sub_kind (result.whole2src & result.whole2dst))) {
			return const_cast <void *> (result.dst_ptr);
		}
		if (contained_nonvirtual_p (result.whole2src)) {
			return NULL;
		}
		if (result.dst2src == __class_type_info::__unknown) {
			result.dst2src = dst_type->__find_public_src (src2dst, result.dst_ptr, src_type, src_ptr);
		}
		if (contained_public_p (result.dst2src)) {
			return const_cast <void *> (result.dst_ptr);
		}
		return NULL;
	}

}
